﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using NHibernate;
using NHibernate.Criterion;
using ProgressTracker.Data.Concrete;
using ProgressTracker.Data.Entities;

namespace ProgressTracker.Tests.Domain.Concrete
{
    [TestClass]
    public class TaskRepositoryTests
    {
        private Mock<ISession> _session;
        private Mock<ITransaction> _transaction;

        private Task _testTask;
        private const int _testUserId = 1;

        [TestInitialize()]
        public void Initialize()
        {
            _session = new Mock<ISession>();
            _transaction = new Mock<ITransaction>();

            _transaction.Setup(x => x.Commit()).Verifiable();
            _session.Setup(x => x.BeginTransaction()).Returns(_transaction.Object);

            _testTask = new Task
            {
                Id = 1,
                Name = "Test",
                Description = "Description"
            };
        }

        [TestMethod]
        public void Test_Add_Calls_Save()
        {
            _session.Setup(x => x.Save(It.Is<Task>(
                t => t.Id == _testTask.Id))).Verifiable();

            var repo = new TaskRepository(_session.Object);
            repo.Add(_testTask);

            _session.Verify();
        }

        [TestMethod]
        public void Test_Add_Commits_Change()
        {
            _session.Setup(x => x.Save(It.IsAny<Task>()));

            var repo = new TaskRepository(_session.Object);
            repo.Add(_testTask);

            _transaction.Verify();
        }

        [TestMethod]
        public void Test_GetTaskById_Returns_Correct_Result()
        {
            _session.Setup(x => x.Get<Task>(_testTask.Id))
                .Returns(_testTask).Verifiable();

            var repo = new TaskRepository(_session.Object);
            var result = repo.GetTaskById(_testTask.Id);

            _session.Verify();
            Assert.AreEqual(_testTask, result);
        }

        [TestMethod]
        public void Test_GetCurrentTasks_Returns_Only_Non_Archived_Tasks()
        {
            _session.Setup(x => x.CreateCriteria<Task>()
                .Add(It.IsAny<SimpleExpression>())
                .Add(Restrictions.Eq("Archived", false))
                .List<Task>())
                .Returns(new Task[0]).Verifiable();

            var repo = new TaskRepository(_session.Object);
            var results = repo.GetCurrentTasks(_testUserId);

            _session.Verify();
        }

        [TestMethod]
        public void Test_GetCurrentTasks_Returns_Only_Tasks_For_User()
        {
            _session.Setup(x => x.CreateCriteria<Task>()
                .Add(Restrictions.Eq("UserId", _testUserId))
                .Add(It.IsAny<SimpleExpression>())
                .List<Task>())
                .Returns(new Task[0]).Verifiable();

            var repo = new TaskRepository(_session.Object);
            var results = repo.GetCurrentTasks(_testUserId);

            _session.Verify();
        }

        [TestMethod]
        public void Test_Update_Calls_Update_For_Item()
        {
            _session.Setup(x => x.Update(It.Is<Task>(
                t => t.Id == _testTask.Id))).Verifiable();

            var repo = new TaskRepository(_session.Object);
            repo.Update(_testTask);

            _session.Verify();
        }

        [TestMethod]
        public void Test_Update_Commits_Transaction()
        {
            _session.Setup(x => x.Update(It.IsAny<Task>()));

            var repo = new TaskRepository(_session.Object);
            repo.Update(_testTask);

            _transaction.Verify();
        }

        [TestMethod]
        public void Test_Delete_Calls_Delete_For_Item()
        {
            _session.Setup(x => x.Delete(It.Is<Task>(
                t => t.Id == _testTask.Id))).Verifiable();

            var repo = new TaskRepository(_session.Object);
            repo.Delete(_testTask);

            _session.Verify();
        }

        [TestMethod]
        public void Test_Delete_Commits_Transaction()
        {
            _session.Setup(x => x.Delete(It.IsAny<Task>()));

            var repo = new TaskRepository(_session.Object);
            repo.Delete(_testTask);

            _transaction.Verify();
        }
    }
}
